package com.fdm.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fdm.model.DAO._AccountDAO;
import com.fdm.model.DAO.DAOFactory;
import com.fdm.model.accounts._Account;
import com.fdm.model.accounts.AccountFactory;
import com.fdm.model.exceptions.BankManagerException;
import com.fdm.model.exceptions.CloseAccountException;
import com.fdm.model.exceptions.InvalidAccountException;

public class BankModelAdapter 
{
	private _AccountDAO accountsDAO;
	private _AccountNumberGenerator accountNumberGenerator;
	private BankAccountOutputVOBuilder accountOutputVOBuilder;
	
	public BankModelAdapter() throws BankManagerException
	{
		accountsDAO = new DAOFactory().getDAO();
		int maxAccount = accountsDAO.getMaxAccountNumber();
		accountNumberGenerator = new AccountNumberGenerator(maxAccount);
		accountOutputVOBuilder = new BankAccountOutputVOBuilder();
	}
	
	public BankModelAdapter(_AccountDAO accountsDAO,
			_AccountNumberGenerator accountNumberGenerator,
			BankAccountOutputVOBuilder accountOutputVOBuilder) {
		this.accountsDAO = accountsDAO;
		this.accountNumberGenerator = accountNumberGenerator;
		this.accountOutputVOBuilder = accountOutputVOBuilder;
	}

	public BankModelAdapter(DAOFactory factory, _AccountDAO accounts,
			AccountNumberGenerator generator, BankAccountOutputVOBuilder voBuilder)
	{
		this.injectDepencies(factory, accounts, generator, voBuilder);
	}
	
	public void setAccounts(_AccountDAO accounts) {
		this.accountsDAO = accounts;
	}

	public void setAccountNumberGenerator(
			AccountNumberGenerator accountNumberGenerator) {
		this.accountNumberGenerator = accountNumberGenerator;
	}

	public void setAccountOutputVOBuilder(
			BankAccountOutputVOBuilder accountOutputVOBuilder) {
		this.accountOutputVOBuilder = accountOutputVOBuilder;
	}

	public void injectDepencies(DAOFactory factory, _AccountDAO accounts,
			AccountNumberGenerator generator, BankAccountOutputVOBuilder voBuilder)
	{
		this.setAccountNumberGenerator(generator);
		this.setAccountOutputVOBuilder(voBuilder);
		this.setAccounts(accounts);
	}
	
	public BankAccountOutputVO createNewAccount(String accountType,
			String name, double balance,double overdraft) throws BankManagerException
	{
		Map<String,Object> parameters = new HashMap<String,Object>();
		parameters.put("accountType", accountType);
		parameters.put("name",name);
		parameters.put("balance", balance);
		parameters.put("overDraftLimit", overdraft);
		parameters.put("accountNumber",accountNumberGenerator.next());
		
		_Account workingAccount = 
				new AccountFactory().createAccount(accountType, parameters);
		accountsDAO.create(workingAccount);
		return accountOutputVOBuilder.buildVO(workingAccount);
	}
	
	public BankAccountOutputVO closeAccount(String accountNumberS) 
			 throws BankManagerException
	{
		_Account workingAccount = AccountNumberCheck(accountNumberS);
		if (workingAccount.getBalance()==0){
			accountsDAO.delete(workingAccount.getAccountNumber());	
		}
		else throw new CloseAccountException();
		return accountOutputVOBuilder.buildVO(workingAccount);
	}
	
	public BankAccountOutputVO viewAccountSummary(String accountNumberS) 
			 throws BankManagerException
	{
		_Account workingAccount = AccountNumberCheck(accountNumberS);
		if (workingAccount != null)
		return accountOutputVOBuilder.buildVO(workingAccount);
		else throw new InvalidAccountException();
	}
	
	public List<BankAccountOutputVO> listAllAccounts() 
			 throws BankManagerException
	{
		List<BankAccountOutputVO> ListVO = new ArrayList<BankAccountOutputVO>();

		for (_Account account : accountsDAO.retrieveAll())
		{
			if(account != null){
				String accountNumberS = Integer.toString(account.getAccountNumber());
				ListVO.add(viewAccountSummary(accountNumberS));
			}
		}
		return ListVO;
	}
	
	public BankAccountOutputVO deposit(String accountNumberS, 
			double amount) throws BankManagerException
	{
		_Account workingAccount = AccountNumberCheck(accountNumberS);
		workingAccount.deposit(amount);
		accountsDAO.update(workingAccount.getAccountNumber(),
				workingAccount.getBalance());
		return accountOutputVOBuilder.buildVO(workingAccount);
	}
	
	public BankAccountOutputVO withdraw(String accountNumberS, 
			double amount) throws BankManagerException
	{
		_Account workingAccount = AccountNumberCheck(accountNumberS);
		workingAccount.withdraw(amount);
		accountsDAO.update(workingAccount.getAccountNumber(),
				workingAccount.getBalance());		
		return accountOutputVOBuilder.buildVO(workingAccount);
	}
	
/*	This will handle the exception that is thrown from parseInt method
	if accountNumberS contains letters/symbols. Used in deposit,withdraw,
	close and view _Account methods.*/	
	private _Account AccountNumberCheck(String accountNumberS) 
			 throws BankManagerException
	{
		try
		{
			int accountNumber = Integer.parseInt(accountNumberS);
			_Account account = accountsDAO.retrieve(accountNumber);
			if (account == null){
				throw new InvalidAccountException();
			}
			return account;
		}
		catch (NumberFormatException e)
		{
			throw new InvalidAccountException();
		}
	}
}
